﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Styx;
using Styx.CommonBot.Frames;
using Styx.CommonBot.POI;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

namespace PPHelper
{
    internal class StuckHandler
    {
        public static void CheckForStucks()
        {
            if (!SWaitTimer.IsRunning)
            {
                Main.Log("Starting CheckStuck Timer");
                SWaitTimer.Start();
                return;
            }

            if (Me.IsResting || Me.Combat) // Handles resting and combat
            {
                LastKnownLocation = Me.Location;
                Reset();
                return;
            }

            if (LastKnownLocation == WoWPoint.Empty)
            {
                Main.Log("Initializing LastKnownLocation");
                LastKnownLocation = Me.Location;
                return;
            }

            if (LastKnownLocation.Distance(Me.Location) > 5)
            {
                //Main.Log(string.Format("Retrieving new current Location : {0}", Me.Location));
                LastKnownLocation = Me.Location;
                Reset();
                return;
            }

            if (SWaitTimer.Elapsed.TotalSeconds > 15 && Me.Location.Distance(ZonesSafeSpots[Me.MapId]) > 10)
            {
                Unstick();
            }
        }

        public static bool IsStuck
        {
            get
            {
                return LastKnownLocation.Distance(Me.Location) < 3 &&
                       !ZonesSafeSpots.Any(z => Me.Location.Distance(z.Value) < 3) && // If not located near safe spots (to avoid false positives)
                       !Me.Combat &&
                       !Me.IsCasting &&
                       !Me.IsChanneling &&
                       //!Me.IsSwimming && Removing this for testing purposes
                       !Me.HasAura(15007) &&
                       !QuestFrame.Instance.IsVisible &&
                       !Helpers.IsTravelling() &&
                       !Me.HasAura("Food") &&
                       !Me.HasAura("Drink") &&
                       BotPoi.Current.Type != PoiType.Sell &&
                       Me.ZoneId != 493 &&
                       Me.HealthPercent > 50; // to avoid resting stucks
            }
        }

        public static void Unstick()
        {
            if (!IsStuck) return;

            StuckCount++;
            Main.Log("We are Stuck ! "); // (Stuck #" + StuckCount + ")

            if (CommonStuckAreas.Any(kvp => Me.Location.Distance(kvp.Key) < 3))
            {
                WoWPoint myNewUnStuckWoWPoint = CommonStuckAreas.First(kvp => Me.Location.Distance(kvp.Key) < 3).Value;
                MoveResult hasMoved;

                if (myNewUnStuckWoWPoint != WoWPoint.Empty)
                {
                    do
                    {
                        float x, y, z;
                        do
                        {
                            x = myNewUnStuckWoWPoint.X + GenerateRandomFloat(-10, 10);
                            y = myNewUnStuckWoWPoint.Y + GenerateRandomFloat(-10, 10);
                        } while (!Navigator.FindHeight(x, y, out z));
                        myNewUnStuckWoWPoint = new WoWPoint(x, y, z);
                    } while (!Navigator.CanNavigateFully(Me.Location, myNewUnStuckWoWPoint));

                    hasMoved = Navigator.MoveTo(myNewUnStuckWoWPoint);
                }
                else
                {
                    hasMoved = MoveResult.Failed;
                }

                if (hasMoved != MoveResult.ReachedDestination)
                {
                    Main.Log(string.Format("Seems like we can't reach the generated WoWPoint ({0}), looking forward to use hearthstone instead.", myNewUnStuckWoWPoint));
                    if (Helpers.CanUseHearthStoneSafely || IsStuckUnderLagonStairs)
                    {
                        WoWMovement.MoveStop();
                        BotPoi.Current.Type = PoiType.None;
                        ObjectManager.GetObjectsOfTypeFast<WoWItem>().Where(item => !StyxWoW.Me.IsCasting && !StyxWoW.Me.IsChanneling).First(item => item.Entry == 6948 && item.CooldownTimeLeft == TimeSpan.Zero).Use();
                    }
                    else
                        Main.Log("We gonna let the bot try to unstuck itself, atm we can't do anything... :(");
                }
            }
            else
            {
                Main.Log("----COPY THE FOLLOWING INFORMATIONS SOMEWHERE ----");
                Main.Log("Seems like the stuck location is unknown, here is the informations about the stuck location :");
                Main.Log(String.Format("Coords : {0}, {1}, {2}", Me.Location.X, Me.Location.Y, Me.Location.Z));
                Main.Log(String.Format("Map : {0} (ID : {1})", Me.MapName, Me.MapId));
                Main.Log(String.Format("SubZone : {0}", Me.SubZoneText));
            }      

            /*
            do
            {
                float x, y, z;
                do
                {
                    x = Me.Location.X + GenerateRandomFloat(-30, 30);
                    y = Me.Location.Y + GenerateRandomFloat(-30, 30);
                } while (!Navigator.FindHeight(x, y, out z));
                myNewWoWPoint = new WoWPoint(x, y, z);
            } while (!Navigator.CanNavigateFully(Me.Location, myNewWoWPoint));
            */

            Reset();
        }

        #region GlobalVars

        public static WoWPoint LastKnownLocation = WoWPoint.Empty;
        public static Stopwatch SWaitTimer = new Stopwatch();
        public static int StuckCount;

        public static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        public static bool IsStuckUnderLagonStairs
        {
            get { return Me.Location.Distance(CommonStuckAreas.First().Key) < 2; }
        }

        public static Dictionary<uint, WoWPoint> ZonesSafeSpots = new Dictionary<uint, WoWPoint>
        {
            {870, new WoWPoint(-3003.91162, 897.0298, 5.35728168)} // Lagon
        };

        public static Dictionary<WoWPoint, WoWPoint> CommonStuckAreas = new Dictionary<WoWPoint, WoWPoint>
        {
            // this dic works like this : { known stuck point, usual unstuck point }

            { new WoWPoint(-2911.77, 927.7809, 9.728539), WoWPoint.Empty }, // Lagon's Stairs (don't move it anywahere, else shit happens)
            { new WoWPoint(-2908.639, 930.5046, 11.97413), new WoWPoint(-2919.119, 924.1943, 9.313871) },    // Lagon's hut
        };

        #endregion

        #region Global Functions
       
        public static void Reset()
        {
            SWaitTimer.Restart();
        }

        public static float GenerateRandomFloat(float pMinVal, float pMaxVal)
        {
            // nextfloat is normally exclusive of the top value (pMaxVal), so add 1
            // to
            // make it inclusive

            int rndFlt = GenerateRandomInt((int) (pMinVal*100), (int) (pMaxVal*100));
            float randomNumber = rndFlt/100.0f;
            return randomNumber;
        }

        private static int GenerateRandomInt(int p1, int p2)
        {
            return new Random().Next(p1, p2);
        }

        #endregion
    }
}